package com.example.chatrecommendation.tool;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.chatrecommendation.bean.dto.Messages;
import com.example.chatrecommendation.bean.entity.Agent;
import com.example.chatrecommendation.bean.entity.Resource;
import com.example.chatrecommendation.bean.vo.PPTLink;
import com.example.chatrecommendation.bean.vo.VideoLink;
import com.example.chatrecommendation.mapper.ResourceMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.json.JSONException;
import org.json.JSONObject;
import org.neo4j.driver.types.Node;
import org.neo4j.driver.types.Relationship;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author Jasmine-drs
 * @date 2024/5/22 16:16
 */
@Slf4j
@Component
public class Api {
	//	@Value("${DASHSCOPE_API_KEY}")
	private String API_KEY = "sk-24942c4acc9b4663b4207293520d79a0";
	//	@Value("${URL_STR}")
	private String URL_STR = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation";

	private final Neo4jCypherExecutor neo4jCypherExecutor;
	private final ResourceMapper resourceMapper;

	public Api(Neo4jCypherExecutor neo4jCypherExecutor, ResourceMapper resourceMapper) {
		this.neo4jCypherExecutor = neo4jCypherExecutor;
		this.resourceMapper = resourceMapper;
	}

	/**
	 * 大模型获取知识图谱
	 *
	 * @param message 用户消息
	 * @return 知识图谱数据
	 */
	public List<Map<String, Object>> getKnowledgeGraph(List<Messages> message) {
		URL url;
		try {
			url = new URL(URL_STR);
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return null;
		}
		HttpURLConnection connection;
		try {
			connection = (HttpURLConnection) url.openConnection();
			connection.setRequestMethod("POST");
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}

		connection.setRequestProperty("Content-Type", "application/json");
		connection.setRequestProperty("Authorization", "Bearer " + API_KEY);
		connection.setDoOutput(true);

//		1. 调整大模型最开始的系统级别提示,解析列表user输入，识别实体
		message.get(0).setContent("请根据以下信息，识别出其中的实体，并返回实体列表，如果无法识别，超出了知识范围，必须返回空字符串，例如：\\n用户提问：Java中的集合框架有哪些常用的接口和类？\\n对应的返回是：\\nJava,集合,框架,常用,接口,类");
//		message.get(0).setContent("请根据用户输入的语句，识别出其中的实体，并构造适用于neo4j的查询语句，注意语句中标签名必须全为中文，如果无法识别,超出了知识范围，必须返回空字符串.例如：\\n用户提问：Java中的集合框架有哪些常用的接口和类？\\n对应的查询语句：MATCH (n)\\n" +
//				"WHERE n:接口 OR n:类 OR ANY(label IN labels(n) WHERE label ENDS WITH '集合')\\n" +
//				"OPTIONAL MATCH (n)-[r]->(related)\\n" +
//				"RETURN n, r, related\\n");
		String jsonInputString = String.format("{\"model\": \"qwen2.5-3b-instruct\", \"input\": {\"messages\": %s}, \"parameters\": {\"result_format\": \"message\"}}", List.of(message.get(0), message.get(message.size() - 1)));//   只去消息记录的第一条和最后一条，为了减少模型提取关键字的复杂度
		System.out.println("getKnowledgeGraph jsonInputString = " + jsonInputString);

		try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
			wr.write(jsonInputString.getBytes(StandardCharsets.UTF_8));
			wr.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}

		StringBuilder response = new StringBuilder();
		try (BufferedReader in = new BufferedReader(
				new InputStreamReader(connection.getInputStream()))) {
			String inputLine;
			while ((inputLine = in.readLine()) != null) {
				response.append(inputLine);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		System.out.println(response);
		connection.disconnect();

//		图数据库查询语句
		String content = response.substring(response.indexOf("\"content\":\"") + 11, response.indexOf("\"}}]},"));
		content = content.replace("\\n", " ");
		System.out.println("content = " + content);
		String[] split = content.split(",");

//      执行图数据库查询语句
		List<Map<String, Object>> maps;
		try {
			//		2. 根据识别出来的实体，查找知识图谱节点，并返回节点信息
			maps = executeQueryWithLabels(List.of(split));
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		for (Map<String, Object> row : maps) {
			for (Map.Entry<String, Object> entry : row.entrySet()) {
				String columnName = entry.getKey();
				Object columnValue = entry.getValue();
				System.out.println(columnName + ": " + columnValue);
				log.info("columnName = " + columnName + " columnValue = " + columnValue);
			}
		}
//		如果maps的元素数量超过了100个，则截取前99个
		if (maps.size() >= 60) {
			maps = maps.subList(0, 60);
		}
		return maps;
	}

	/**
	 * 使用大模型获取ppt
	 *
	 * @param message 用户消息
	 * @return ppt链接数组
	 */
	public Pair<List<PPTLink>, List<VideoLink>> getPPTAndVideo(List<Messages> message) {
		URL url;
		try {
			url = new URL(URL_STR);
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return null;
		}
		HttpURLConnection connection;
		try {
			connection = (HttpURLConnection) url.openConnection();
			connection.setRequestMethod("POST");
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}

		connection.setRequestProperty("Content-Type", "application/json");
		connection.setRequestProperty("Authorization", "Bearer " + API_KEY);
		connection.setDoOutput(true);

//		1. 调整大模型最开始的系统级别提示,解析列表user输入，识别ppt文件名
//		message.get(0).setContent("请根据以下信息，识别出用户询问的内容可能存在于《Java面向对象编程》这门课程的哪个ppt里面，需要得出至少两个可能的ppt名称，只需要名称不需要.pptx后缀，例如：\\n用户提问：Java中的集合框架有哪些常用的接口和类？\\n对应的返回是：\\n数组,封装性,类与对象,常用JavaAPI之集合类");
		message.get(0).setContent("请根据用户提出的内容，分析其中最可能以及最重要的java基础知识点名称，例如：\\n用户提问：Java中的集合框架有哪些常用的接口和类？\\n对应的返回是：\\n数组,类与对象,常用JavaAPI之集合类,接口,常用\\n禁止事项：不能出现类似于'列表(List)'中文后面括号英文的情况");
		String jsonInputString = String.format("{\"model\": \"qwen2.5-3b-instruct\", \"input\": {\"messages\": %s}, \"parameters\": {\"result_format\": \"message\"}}", List.of(message.get(0), message.get(message.size() - 1)));//   只去消息记录的第一条和最后一条，为了减少模型提取关键字的复杂度
		System.out.println("getPPTAndVideo jsonInputString = " + jsonInputString);

		try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
			wr.write(jsonInputString.getBytes(StandardCharsets.UTF_8));
			wr.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}

		StringBuilder response = new StringBuilder();
		try (BufferedReader in = new BufferedReader(
				new InputStreamReader(connection.getInputStream()))) {
			String inputLine;
			while ((inputLine = in.readLine()) != null) {
				response.append(inputLine);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println(response);
		connection.disconnect();

//		解析字符串
		String content = response.substring(response.indexOf("\"content\":\"") + 11, response.indexOf("\"}}]},"));
		content = content.replace("\\n", " ");
		String[] split = content.split(",");
		System.out.println("split = " + Arrays.toString(split));
		LambdaQueryWrapper<Resource> wrapper = new LambdaQueryWrapper<>();
// 查询type为1的资源
		wrapper.eq(Resource::getType, 1);
// 添加模糊匹配的条件
		wrapper.and((LambdaQueryWrapper<Resource> w) -> {
			for (int i = 0; i < split.length; i++) {
				if (i > 0) {
					w.or();
				}
				w.like(Resource::getName, split[i]);
			}
		});
		List<Resource> resources = resourceMapper.selectList(wrapper);
		List<PPTLink> pptLinks = new ArrayList<>();
		if (resources.size() > 0) {
			for (Resource resource : resources) {
				PPTLink pptLink = new PPTLink();
				pptLink.setName(resource.getName());
				pptLink.setSrc(resource.getCover());
				pptLink.setPptSrc(resource.getValue());
				pptLinks.add(pptLink);
			}
//			如果pptlinks元素大于2则调整为2
			if (pptLinks.size() > 2) {
//				取前两个元素
				pptLinks = pptLinks.subList(0, 2);
			}
		}

		LambdaQueryWrapper<Resource> wrapper1 = new LambdaQueryWrapper<>();
//		查询视频
		wrapper1.eq(Resource::getType, 0);
		wrapper1.and(i -> i.notLike(Resource::getName, "%异常%")); // 排除包含“异常”关键字的资源

		// 添加模糊匹配的条件
		wrapper1.and((LambdaQueryWrapper<Resource> w) -> {
			for (int i = 0; i < split.length; i++) {
				if (i > 0) {
					w.or();
				}
				w.like(Resource::getName, split[i]);
			}
		});
		List<Resource> resources1 = resourceMapper.selectList(wrapper1);
		List<VideoLink> videoLinks = new ArrayList<>();
		if (resources1.size() > 0) {
			for (Resource resource : resources1) {
				VideoLink videoLink = new VideoLink();
				videoLink.setName(resource.getName());
				videoLink.setSrc(resource.getCover());
				videoLink.setVideoSrc(resource.getValue());
				videoLinks.add(videoLink);
			}
//			如果元素大于2，则调整为2
			if (videoLinks.size() > 2) {
//				取前两位元素
				videoLinks = videoLinks.subList(0, 2);
			}
		}
		return Pair.of(pptLinks, videoLinks);
	}

	public Agent getAgent(String description) {
		URL url;
		try {
			url = new URL(URL_STR);
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return null;
		}
		HttpURLConnection connection;
		try {
			connection = (HttpURLConnection) url.openConnection();
			connection.setRequestMethod("POST");
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}

		connection.setRequestProperty("Content-Type", "application/json");
		connection.setRequestProperty("Authorization", "Bearer " + API_KEY);
		connection.setDoOutput(true);
		List<Messages> message = new ArrayList<>();
		message.add(new Messages("system", "现在你是一个创建智能体配置的助手，请根据用户的输入期望，生成智能体的name、description、config以及三个推荐问题recommend1、recommend2、recommend3，记住只能生成要求的这些项目。要求为json格式，尽量全是中文\\n用户输入："));
		message.add(new Messages("user", description));
//		1. 调整大模型最开始的系统级别提示,解析列表user输入，识别ppt文件名
		String jsonInputString = String.format("{\"model\": \"qwen2.5-3b-instruct\", \"input\": {\"messages\": %s}, \"parameters\": {\"result_format\": \"message\"}}", message);
		System.out.println("getAgent jsonInputString = " + jsonInputString);

		try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
			wr.write(jsonInputString.getBytes(StandardCharsets.UTF_8));
			wr.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}

		StringBuilder response = new StringBuilder();
		try (BufferedReader in = new BufferedReader(
				new InputStreamReader(connection.getInputStream()))) {
			String inputLine;
			while ((inputLine = in.readLine()) != null) {
				response.append(inputLine);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println(response);
		connection.disconnect();
//      解析字符串
		String content = response.substring(response.indexOf("```json") + 9, response.indexOf("\\n```"));
		content = content.replace("\\n", " ")
				.replace("\\", " ")
				.replace(" ", "");
		System.out.println("content = " + content);
		String name;
		String description1;
		JSONObject config;
		String recommend1;
		String recommend2;
		String recommend3;
		String configStr;
		try {
			JSONObject jsonObj = new JSONObject(content);

			// 提取名称
			name = jsonObj.getString("name");
			System.out.println("名称: " + name);
			// 提取描述
			description1 = jsonObj.getString("description");
			System.out.println("描述: " + description1);

			// 提取配置信息
			config = jsonObj.getJSONObject("config");
			System.out.println("配置信息: " + config);
//		    替换配置中json格式的双引号，替换为单引号，为接下来的传递给大模型
			configStr = config.toString().replace("\"", "'");
			// 提取推荐问题
			recommend1 = jsonObj.getString("recommend1");
			recommend2 = jsonObj.getString("recommend2");
			recommend3 = jsonObj.getString("recommend3");
			System.out.println("推荐问题1: " + recommend1);
			System.out.println("推荐问题2: " + recommend2);
			System.out.println("推荐问题3: " + recommend3);
		} catch (JSONException e) {
			e.printStackTrace();
			throw new RuntimeException("JSON解析错误，模型生成json数据有概率出错，属于正常现象，请重试");
		}
		Agent agent = new Agent();
		agent.setName(name);
		agent.setDescription(description1);
		agent.setConfig(configStr);
		agent.setRecommend1(recommend1);
		agent.setRecommend2(recommend2);
		agent.setRecommend3(recommend3);
		return agent;
	}

	/**
	 * 大模型回答问题
	 *
	 * @param message 用户消息
	 * @return 答案
	 */
	public String callLargeModel(List<Messages> message) {
		URL url;
		try {
			url = new URL(URL_STR);
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return "";
		}
		HttpURLConnection connection;
		try {
			connection = (HttpURLConnection) url.openConnection();
			connection.setRequestMethod("POST");
		} catch (IOException e) {
			e.printStackTrace();
			return "";
		}

		connection.setRequestProperty("Content-Type", "application/json");
		connection.setRequestProperty("Authorization", "Bearer " + API_KEY);
		connection.setDoOutput(true);

//		5. 调用大模型回答用户问题
//		String jsonInputString = String.format("{\"model\": \"qwen-turbo\", \"input\": {\"messages\": [{\"role\": \"system\", \"content\": \"你是个乐于助人的助手。\"}, {\"role\": \"user\", \"content\": \"%s\"}]}, \"parameters\": {\"result_format\": \"message\"}}",UserInput);
		String jsonInputString = String.format("{\"model\": \"qwen2.5-3b-instruct\", \"input\": {\"messages\": %s}, \"parameters\": {\"result_format\": \"message\"}}", message);
		System.out.println("callLargeModel jsonInputString = " + jsonInputString);

		try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
			wr.write(jsonInputString.getBytes(StandardCharsets.UTF_8));
			wr.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}

		StringBuilder response = new StringBuilder();
		try (BufferedReader in = new BufferedReader(
				new InputStreamReader(connection.getInputStream()))) {
			String inputLine;
			while ((inputLine = in.readLine()) != null) {
				response.append(inputLine);
			}
		} catch (IOException e) {
			e.printStackTrace();
			return "请求失败，网络异常";
		}

		System.out.println(response);
		connection.disconnect();
//		解析大模型的回答
		String content = response.substring(response.indexOf("\"content\":\"") + 11, response.indexOf("\"}}]},"));
		return content;
	}

	public String optimizeQuestion(String question) {
		URL url;
		try {
			url = new URL(URL_STR);
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return null;
		}
		HttpURLConnection connection;
		try {
			connection = (HttpURLConnection) url.openConnection();
			connection.setRequestMethod("POST");
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}

		connection.setRequestProperty("Content-Type", "application/json");
		connection.setRequestProperty("Authorization", "Bearer " + API_KEY);
		connection.setDoOutput(true);
		List<Messages> message = new ArrayList<>();
		message.add(new Messages("system", "你现在是一个提示词优化助手，请你优化用户的输入内容，绝对不能直接回答!绝对不能直接回答!绝对不能直接回答!要求使用思维链的方式优化语句，即尽可能的让用户理解，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答，不要直接回答。\\n用户输入："));
		message.add(new Messages("user", question));
//		1. 调整大模型最开始的系统级别提示,解析列表user输入，识别ppt文件名
		String jsonInputString = String.format("{\"model\": \"qwen2.5-3b-instruct\", \"input\": {\"messages\": %s}, \"parameters\": {\"result_format\": \"message\"}}", message);
		System.out.println("optimizeQuestion jsonInputString = " + jsonInputString);

		try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
			wr.write(jsonInputString.getBytes(StandardCharsets.UTF_8));
			wr.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}

		StringBuilder response = new StringBuilder();
		try (BufferedReader in = new BufferedReader(
				new InputStreamReader(connection.getInputStream()))) {
			String inputLine;
			while ((inputLine = in.readLine()) != null) {
				response.append(inputLine);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println(response);
		connection.disconnect();
//      解析字符串
		String content = response.substring(response.indexOf("\"content\":\"") + 11, response.indexOf("\"}}]},"));
		System.out.println("content = " + content);
		return content;
	}

	public String getChatRecordsName(List<Messages> message) {
		URL url;
		try {
			url = new URL(URL_STR);
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return "";
		}
		HttpURLConnection connection;
		try {
			connection = (HttpURLConnection) url.openConnection();
			connection.setRequestMethod("POST");
		} catch (IOException e) {
			e.printStackTrace();
			return "";
		}

		connection.setRequestProperty("Content-Type", "application/json");
		connection.setRequestProperty("Authorization", "Bearer " + API_KEY);
		connection.setDoOutput(true);

		message.get(0).setContent("请根据聊天记录生成一个聊天记录名称，字数在十个字以内");
//		5. 调用大模型回答用户问题
		String jsonInputString = String.format("{\"model\": \"qwen2.5-3b-instruct\", \"input\": {\"messages\": %s}, \"parameters\": {\"result_format\": \"message\"}}", message);
		System.out.println("getChatRecordsName jsonInputString = " + jsonInputString);

		try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
			wr.write(jsonInputString.getBytes(StandardCharsets.UTF_8));
			wr.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}

		StringBuilder response = new StringBuilder();
		try (BufferedReader in = new BufferedReader(
				new InputStreamReader(connection.getInputStream()))) {
			String inputLine;
			while ((inputLine = in.readLine()) != null) {
				response.append(inputLine);
			}
		} catch (IOException e) {
			e.printStackTrace();
			return "请求失败，网络异常";
		}

		System.out.println(response);
		connection.disconnect();
//		解析大模型的回答
		String name = response.substring(response.indexOf("\"content\":\"") + 11, response.indexOf("\"}}]},"));
		return name;
	}

	private List<Map<String, Object>> executeQueryWithLabels(List<String> labels) {
		// 动态构建Cypher查询
		StringBuilder queryBuilder = new StringBuilder();
		queryBuilder.append("MATCH (n) WHERE ");

		for (int i = 0; i < labels.size(); i++) {
			String label = labels.get(i);
			queryBuilder.append("ANY(l IN labels(n) WHERE l CONTAINS '").append(label).append("')");
			if (i < labels.size() - 1) {
				queryBuilder.append(" OR ");
			}
		}

		queryBuilder.append(" OPTIONAL MATCH (n)-[r]->(related) RETURN n, r, related");

		String query = queryBuilder.toString();

		List<Map<String, Object>> res = neo4jCypherExecutor.executeCypherQuery(query);
		return transformResult(res);
	}

	private List<Map<String, Object>> transformResult(List<Map<String, Object>> rawResult) {
		return rawResult.stream().map(this::transformRow).toList();
	}

	private Map<String, Object> transformRow(Map<String, Object> row) {
		Map<String, Object> transformedRow = new HashMap<>();

		row.forEach((key, value) -> {
			if (value instanceof Node) {
				transformedRow.put(key, transformNode((Node) value));
			} else if (value instanceof Relationship) {
				transformedRow.put(key, transformRelationship((Relationship) value));
			} else {
				transformedRow.put(key, value);
			}
		});

		return transformedRow;
	}

	private Map<String, Object> transformNode(Node node) {
		Map<String, Object> nodeMap = new HashMap<>();
		nodeMap.put("id", node.id());
		nodeMap.put("labels", node.labels());
		nodeMap.put("properties", node.asMap());
		return nodeMap;
	}

	private Map<String, Object> transformRelationship(Relationship relationship) {
		Map<String, Object> relationshipMap = new HashMap<>();
		relationshipMap.put("id", relationship.id());
		relationshipMap.put("startNodeId", relationship.startNodeId());
		relationshipMap.put("endNodeId", relationship.endNodeId());
		relationshipMap.put("type", relationship.type());
		relationshipMap.put("properties", relationship.asMap());
		return relationshipMap;
	}
}
